package com.cm.study.springboot.zk.client.zk;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ZooKeepers {
	private final static Logger logger = LoggerFactory.getLogger(ZooKeepers.class);
	private static String CLIENT_IP = "192.168.56.99";
	private static String CLIENT_PORT = "2181";
	private static final int CONNECTION_TIMEOUT = 15000;
	private static ZooKeepers zooKeeperHelper = null;
	private static ZooKeeper zk = null;
	public static void setConfig(String clientId,String clientPort){
		CLIENT_IP = clientId;
		CLIENT_PORT = clientPort;
		ZooKeepers.getInstants().fresh();
	}
	public static ZooKeepers getInstants(){
		if (zooKeeperHelper == null) {
			zooKeeperHelper = new ZooKeepers();
		}
		return zooKeeperHelper;
	}
	private ZooKeepers() {
		init();
	}
	private void init() {
		if (zk == null) {
			try {
				zk = new ZooKeeper(CLIENT_IP, CONNECTION_TIMEOUT,new Watcher() {
				//监控所有被触发的事件
					@Override
					public void process(WatchedEvent event) {
						logger.debug("已经触发了" + event.getType() + "事件！");
					}
				});
			} catch (IOException e) {
				logger.error("zookeeper操作失败：",e);
			}
		}
	}
	public void fresh(){
		close();
		init();
	}
	private void close(){
		if (zk == null) {
			try {
				zk.close();
				zk  = null;
			} catch (InterruptedException e) {
				logger.error("zookeeper操作失败：",e);
			}
		}
	}
	public ZooKeeper getZooKeeper()  {
		return zk;
	}
	/**
	 * 创建一个给定的目录节点 path, 并给它设置数据，
	 * CreateMode 标识有四种形式的目录节点，
	 * 分别是 PERSISTENT：持久化目录节点，
	 * 这个目录节点存储的数据不会丢失；
	 * PERSISTENT_SEQUENTIAL：顺序自动编号的目录节点，这种目录节点会根据当前已近存在的节点数自动加 1，
	 * 然后返回给客户端已经成功创建的目录节点名；EPHEMERAL：临时目录节点，
	 * 一旦创建这个节点的客户端与服务器端口也就是 session 超时，
	 * 这种节点会被自动删除；EPHEMERAL_SEQUENTIAL：临时自动编号节点
	 */
	public void create(String path, byte[] data,ArrayList<ACL> acl, CreateMode createMode) {
		try {
			if (zk.exists(path, false) == null) {
				String parentPath = path.substring(0, path.lastIndexOf("/"));
				if (!"".equals(parentPath)) {
					this.create(parentPath, null, acl, createMode);
				}
				zk.create(path, data, acl, createMode);
			}
		} catch (KeeperException e) {
			logger.error("zookeeper操作失败：",e);
		} catch (InterruptedException e) {
			logger.error("zookeeper操作失败：",e);
		}
	}
	public void create(String path, byte[] data) {
		this.create(path, data,  Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
	}
	public byte[] getData(String path, boolean watch, Stat stat) {
		try {
            return zk.getData(path, watch,  stat);
        } catch (KeeperException e) {
            logger.error("zookeeper操作失败：",e);
        } catch (InterruptedException e) {
            logger.error("zookeeper操作失败：",e);
        }
		return null;
	}

	/**
	 * 获取指定 path 下的所有子目录节点，同样 getChildren方法也有一个重载方法可以设置特定的 watcher 监控子节点的状态
	 */
	public List<String> getChildren(String path, boolean watch) {
		try {
			return zk.getChildren(path, watch);
		} catch (KeeperException e) {
			logger.error("zookeeper操作失败：",e);
		} catch (InterruptedException e) {
			logger.error("zookeeper操作失败：",e);
		}
		return null;
	}

	/**
	 * 判断某个 path 是否存在，并设置是否监控这个目录节点，这里的 watcher 是在创建 ZooKeeper 实例时指定的 watcher，exists方法还有一个重载方法，可以指定特定的watcher
	 */
	public Stat exists(String path, boolean watch) {
		try {
			return zk.exists(path, watch);
		} catch (KeeperException e) {
			logger.error("zookeeper操作失败：",e);
		} catch (InterruptedException e) {
			logger.error("zookeeper操作失败：",e);
		}
		return null;
	}

	/**
	 * 重载方法，这里给某个目录节点设置特定的 watcher，Watcher 在 ZooKeeper 是一个核心功能，
	 * Watcher 可以监控目录节点的数据变化以及子目录的变化，
	 * 一旦这些状态发生变化，服务器就会通知所有设置在这个目录节点上的 Watcher，
	 * 从而每个客户端都很快知道它所关注的目录节点的状态发生变化，而做出相应的反应
	 */
	public Stat exists(String path, Watcher watch) {
		try {
			return zk.exists(path, watch);
		} catch (KeeperException e) {
			logger.error("zookeeper操作失败：",e);
		} catch (InterruptedException e) {
			logger.error("zookeeper操作失败：",e);
		}
		return null;
	}

	/**
	 * 给 path 设置数据，可以指定这个数据的版本号，如果 version 为 -1 怎可以匹配任何版本
	 */
	public void setData(String path, byte[] data, int version) {
		try {
			zk.setData(path, data, version);
		} catch (KeeperException e) {
			logger.error("zookeeper操作失败：",e);
		} catch (InterruptedException e) {
			logger.error("zookeeper操作失败：",e);
		}
	}

	public  void printTree(String path,Writer writer,String lineSplitChar) {
		try {
			String[] list;
			list = getTree(path);
			Stat stat = new Stat();
			for(String name:list){
				byte[] value = zk.getData(name, false, stat);
				if(value == null){
					writer.write(name + lineSplitChar);
				}else{
					writer.write(name+"[v."+ stat.getVersion() +"]"+"["+ new String(value) +"]"  + lineSplitChar);
				}
			}
		} catch (Exception e) {
			logger.error("zookeeper操作失败：",e);
		}
	}

	public  String printTree(String path) {
		StringWriter writer = new StringWriter();
		String lineSplitChar = "\n";
		ZooKeepers.getInstants().printTree(path, writer, lineSplitChar);
		ZooKeepers.getInstants().printTree(path, writer, lineSplitChar);
		return writer.getBuffer().toString();
	}

	public  void deleteTree(String path) {
		String[] list = getTree(path);
		for(int i= list.length -1;i>=0; i--){
			try {
				zk.delete(list[i],-1);
			} catch (InterruptedException e) {
				logger.error("zookeeper操作失败：",e);
			} catch (KeeperException e) {
				logger.error("zookeeper操作失败：",e);
			}
		}
	}

	public  String[] getTree(String path){
		try {
			if(zk.exists(path, false) == null){
				return new String[0];
			}
			List<String> dealList = new ArrayList<String>();
			dealList.add(path);
			int index =0;
			while(index < dealList.size()){
				String tempPath = dealList.get(index);
				List<String> children = zk.getChildren(tempPath, false);
				if(tempPath.equalsIgnoreCase("/") == false){
					tempPath = tempPath +"/";
				}
				Collections.sort(children);
				for(int i = children.size() -1;i>=0;i--){
					dealList.add(index+1, tempPath + children.get(i));
				}
				index++;
			}
			return (String[])dealList.toArray(new String[0]);
		} catch (KeeperException e) {
			logger.error("zookeeper操作失败：",e);
		} catch (InterruptedException e) {
			logger.error("zookeeper操作失败：",e);
		}
		return null;

	}

	/**
	 * 删除 path 对应的目录节点，version 为 -1 可以匹配任何版本，也就删除了这个目录节点所有数据
	 */
	public void delete(String path, int version) {
		try {
			zk.delete(path, version);
		} catch (InterruptedException e) {
			logger.error("zookeeper操作失败：",e);
		} catch (KeeperException e) {
			logger.error("zookeeper操作失败：",e);
		}
	}


}
